//FAS低代码开发平台
//变更日志插件: 针对CUD操作记录变更日志，其中新增、修改的克隆TRecord逻辑是避免状态机插件【保存后事件】影响返给前端的TRecord内容
//新增：
//    1、前端NewTRecord A传入后【BeforeExecuteSQL】做初次变更比较（没有OldValue），为A生成ChangeSet供状态机【保存前事件】Action脚本使用
//    2、数据落盘后【Finish】，从DB中取出最新NewTRecord B（获取DB默认值），克隆NewTRecord C，将B传入插件，再次做变更比较。
//       此时生成的变更为包含状态机脚本更新后的最新变更，直接生成大数据日志，而后执行状态机的【保存后事件】
//    3、将C返回给前端。
//修改：
//    1、前端NewTRecord A传入后【BeforeExecuteSQL】，获取DB中的OldRecord，做变更比较，将NewTRecord A,OldValue传入供状态机【保存前事件】Action脚本使用
//    2、状态机【保存前事件】执行完毕后（A的ChangeSet已包含最新变更），克隆NewTRecord B，
//       数据落盘后【Finish】，将A传入变更日志插件中，直接生成大数据日志。而后执行状态机的【保存后事件】
//    3、将最新的时间戳赋值给B后返回前端。
//删除：
//    1、由于删除不走状态机逻辑，故在【BeforeExecuteSQL】时，变更日志插件中生成TRecord实例，只记录主键id变更，放到上下文中
//    2、数据落盘后【Finish】，在上下文中拿到生成的TRecord实例，生成大数据日志
//    3、删除不向前端返回数据
//===============================================================================
//变更历史纪录
//日期			版本	修改人	描述
//===============================================================================

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.Base.Service;
using T.FAS.Runtime.DataAccess.Common;
using T.FAS.Runtime.DataAccess.SPI;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.FAS.Runtime.Metadata.Client;
using T.FAS.Runtime.Metadata.SPI;
using T.ServiceComponent.Log.Client;
using T.ServiceComponent.Log.SPI;

namespace T.FAS.Runtime.DataAccess.Engine
{
    /// <summary>
    /// 统一数据访问服务变更日志插件
    /// CDO是否开启变更日志，是否影响ChangeSet生成
    /// </summary>
    public class ChangeLogPlugin : IDataAccessPlugin
    {
        #region Fields & Properties
        private readonly IDACLogService _localLogService = DACLogFactory.GetDACLogService();
        private IMetaDataClientService metaDataClientService = MetaDataClientService.GetInstance();
        private const string pluginName = "变更日志插件";
        private TRecord PrevRecord;
        private InvokeContext invokeContext;
        private TaskFactory TaskFactory = new TaskFactory();

        /// <summary>
        /// 插件注入点
        /// </summary>
        public int InjectionPoint
        {
            get
            {
                var injectionPoint = SPI.InjectionPoint.Finish
                                    //| SPI.InjectionPoint.Entry
                                    //| SPI.InjectionPoint.BeforeExecuteSQL
                                    ;
                                     
                return (int)injectionPoint;
            }
        }
        /// <summary>
        /// 插件注入方法类型
        /// </summary>
        public int InjectionMethodType
        {
            get
            {
                var injectionType = SPI.InjectionMethodType.Create 
                    | SPI.InjectionMethodType.Update
                    //| SPI.InjectionMethodType.Retrieve
                    | SPI.InjectionMethodType.Delete;
                return (int)injectionType;
            }
        }
        public List<string> MetaType => new List<string> { DataModelConst.CDOMetaType};
        public string PluginCode => "ChangeLogPlugin";
        public bool IsCanRepeatExecute => true;
        public InvokeContext InvokeContext
        {
            get
            {
                return invokeContext;
            }
        }

        #endregion

        /// <summary>
        /// 构建执行上下文
        /// </summary>
        /// <param name="invokeContext"></param>
        public void BuildInvokeContext(InvokeContext invokeContext)
        {
            this.invokeContext = invokeContext;
        }

        /// <summary>
        /// 插件执行重载
        /// </summary>
        /// <param name="InjectMethodType"></param>
        /// <param name="InjectPoint"></param>
        /// <returns></returns>
        public ActionResult Execute(InjectionMethodType InjectMethodType, InjectionPoint InjectPoint, DataAccessPluginExtParam extExecuteData = null)
        {
            if (!MetaType.Contains(invokeContext.MetaType))
                return new ActionResult
                {
                    Result = true,
                    Message = $"不符合{pluginName}要求的元数据类型，不予执行"
                };
            if(!ValidateInjectPoint(InjectMethodType, InjectPoint))
            {
                return new ActionResult
                {
                    Result = true,
                    Message = $"不符合{pluginName}要求的执行方法类型或注入点，不予执行"
                };
            }
            if(invokeContext.SourceMeta is CompositeDataObject cdo)
            {
                if (cdo.EnableChangeLog)
                {
                    //变更日志不再执行SQL保存前的变更比较，该逻辑由Action在执行CDO.Action时统一执行
                    #region BeforeExecuteSQL
                    //if (InjectPoint == SPI.InjectionPoint.BeforeExecuteSQL)
                    //{
                    //    if (InjectMethodType == SPI.InjectionMethodType.Update)
                    //    {
                    //        //填充旧Record版本
                    //        FillOldResult(invokeContext, InjectMethodType, extExecuteData);
                    //    }
                    //    //在执行状态机保存前事件之前，记录通用变更日志
                    //    //
                    //    GetChangeLogs(InjectMethodType, extExecuteData);
                    //}
                    #endregion
                    //SQL保存后事件中，直接使用传入的TRecord获取大数据变更日志
                    if (InjectPoint == SPI.InjectionPoint.Finish)
                    {
                        //return FireStateChangedEvent(invokeContext, InjectMethodType, extExecuteData);
                        //记录大数据日志
                        //var changeLogs = GetBigDataChangeLogs(InjectMethodType, extExecuteData);
                        //if(changeLogs != null && changeLogs.Count > 0)
                        //{
                        //    SendDataChangeLogsAsync(changeLogs);
                        //}

                        List<TRecord> result = null;
                        //Create时，需要从DB中重新获取最新TRecord生成大数据变更日志（为了拿到DB默认值）
                        if (InjectMethodType == SPI.InjectionMethodType.Create)
                        {
                            if(extExecuteData == null)
                                throw new FasException(DataAccessErrorCode.PlugIn.ChangeLog.Error, string.Format(CommonStringClassEngine.ChangeLogPlugin_Execute));
                            result = GetChangeLogs(InjectMethodType, extExecuteData);
                            foreach (var r in result)
                            {
                                SetModifyInfo(cdo, r);
                            }
                        }
                        //Update时直接使用传入的TRecord生成大数据变更日志
                        if (InjectMethodType == SPI.InjectionMethodType.Update)
                        {
                            if (extExecuteData == null)
                                throw new FasException(DataAccessErrorCode.PlugIn.ChangeLog.Error, string.Format(CommonStringClassEngine.ChangeLogPlugin_Execute1));
                            SetModifyInfo(cdo, extExecuteData.NewRecord);
                            result = new List<TRecord> { extExecuteData.NewRecord };
                        }
                        //删除时生成一个空的TRecord
                        if (InjectMethodType == SPI.InjectionMethodType.Delete)
                        {
                            //var result = invokeContext.GetData(PluginConst.ChangeLog.Code, PluginConst.ChangeLog.DeleteRecord) as List<TRecord>;
                            result = GetChangeLogs(InjectMethodType, extExecuteData);
                            if (result == null)
                                throw new FasException(DataAccessErrorCode.PlugIn.ChangeLog.Error, string.Format(CommonStringClassEngine.ChangeLogPlugin_Execute2));
                        }
                        if (result != null)
                        {
                            //获取所有变更的TRecord
                            result = result.Where(r => r.ChangeType != TRecordChangeType.UnChanged).ToList();
                            if (result.Count > 0)
                                SendDataChangeLogs(result);
                        }

                        return new ActionResult
                        {
                            Result = true,  //没有开启变更日志的CDO跳过执行
                        };
                    }
                    else
                        return new ActionResult
                        {
                            Result = true,  //没有开启变更日志的CDO跳过执行
                        };
                }
                else
                {
                    return new ActionResult
                    {
                        Result = true,  //没有开启变更日志的CDO跳过执行
                    };
                }
            }
            else
            {
                return new ActionResult
                {
                    Result = true,  //元数据类型不对的不予执行
                };
            }
        }

        #region Common Private Funcs

        /// <summary>
        /// 校验上下文变量正确性,true:校验通过，false:校验失败
        /// </summary>
        /// <param name="invokeContext"></param>
        /// <param name="errorResult"></param>
        /// <returns></returns>
        private bool ValidateContext(InvokeContext invokeContext, out ActionResult errorResult)
        {
            errorResult = new ActionResult
            {
                PluginCode = PluginCode,
                Result = true,
                InvokeContext = invokeContext,
                ActionErrorHandleType = ActionErrorHandleType.Throw,
            };
            if (invokeContext == null)
            {
                errorResult.Result = false;
                errorResult.Message = $"{pluginName}执行异常,invokeContext参数为空";
            }
            var CDO = invokeContext.SourceMeta as CompositeDataObject;
            if (CDO == null)
            {
                errorResult.Result = false;
                errorResult.Message = $"{pluginName}执行异常,invokeContext中的SoureMeta为空或类型不是CDO";
            }
            if (invokeContext.SourceData == null)
            {
                errorResult.Result = false;
                errorResult.Message = $"{pluginName}执行异常,SourceData TRecord参数为空";
            }
            return errorResult.Result;
        }

        private bool ValidateInjectPoint(InjectionMethodType MethodType, InjectionPoint Point)
        {
            return (this.InjectionMethodType & (int)MethodType) == (int)MethodType
                      && (this.InjectionPoint & (int)Point) == (int)Point;

        }
        #endregion

        #region Business Private Funcs
        /// <summary>
        /// 填充PrevRecord字段，从外部获取传入的Old Record，如果未获取到，则从数据库中拉取旧Record
        /// </summary>
        /// <param name="invokeContext"></param>
        /// <param name="InjectMethodType"></param>
        private void FillOldResult(InvokeContext invokeContext, InjectionMethodType InjectMethodType, DataAccessPluginExtParam extExecuteData)
        {
            if(InjectMethodType == SPI.InjectionMethodType.Update)
            {
                var meta = invokeContext.SourceMeta as CompositeDataObject;
                string pkData = default; //int主键也转为string处理
                try
                {
                    var inputOldData = extExecuteData.OldRecord;
                    if (inputOldData == null)
                    {
                        pkData = GetCDOTRecordPrimaryKeyValue(meta, invokeContext.SourceData);
                        var result = DataAccessEngine.GetInstance().CreateCDOQueryService().QueryData(meta, pkData, DACControlContextHelper.BuildDefultExtParams());
                        PrevRecord = ((TCommonResult<TRecord>)result).Record;

                        if (PrevRecord == null || PrevRecord.Count == 0)
                        {
                            throw new Exception(string.Format(CommonStringClassEngine.ChangeLogPlugin_FillOldResult,pluginName,meta.Code,pkData));
                        }
                    }
                    else
                    {
                        PrevRecord = inputOldData;
                    }
                }
                catch (Exception e)
                {
                    throw new FasException(DataAccessErrorCode.PlugIn.ChangeLog.Error, string.Format(CommonStringClassEngine.ChangeLogPlugin_FillOldResult1,pluginName,meta.Code,pkData), e);
                }
            }
        }

        /// <summary>
        /// 取出TRecord中的主节点的主键值
        /// </summary>
        /// <param name="compositeDataObject"></param>
        /// <param name="record"></param>
        /// <returns></returns>
        private string GetCDOTRecordPrimaryKeyValue(CompositeDataObject compositeDataObject, TRecord record)
        {
            try
            {
                var pkCol = compositeDataObject.RootCompositeNodeObject?.DataObject.Columns.FirstOrDefault(c => c.IsPkColumn);
                return record.GetValue(pkCol.ColumnName).ToString();
            }
            catch (Exception e)
            {
                throw new Exception(CommonStringClassEngine.ChangeLogPlugin_GetCDOTRecordPrimaryKeyValue, e);
            }
        }

        /// <summary>
        /// 获取通用变更日志
        /// </summary>
        /// <param name="InjectMethodType"></param>
        /// <param name="extExecuteData"></param>
        private List<TRecord> GetChangeLogs(InjectionMethodType InjectMethodType, DataAccessPluginExtParam extExecuteData)
        {
            var log = ChangeLogFactory.GetInstance(InjectMethodType);
            List<TRecord> changeRecords = new List<TRecord>();
            ChangeLogContext changeLogContext = new ChangeLogContext
            {
                CompositeDataObject = invokeContext.SourceMeta as CompositeDataObject,
                InjectionMethodType = InjectMethodType,

            };
            changeLogContext.InitAuthControl(invokeContext.IsNeedAuth, invokeContext.ColumnPermissionResult);
            //初始化CDO的CNO间引用
            changeLogContext.CompositeDataObject.InitCDO();

            if (InjectMethodType == SPI.InjectionMethodType.Create)
            {
                if (extExecuteData.NewRecord != null)
                {
                    changeLogContext.CurrentData = extExecuteData.NewRecord;
                }
                var changeLog = log.LogChangeSet(changeLogContext);
                if (changeLog != null)
                    changeRecords.Add(changeLog);
            }
            else if (InjectMethodType == SPI.InjectionMethodType.Update)
            {
                if (extExecuteData.NewRecord != null)
                {
                    changeLogContext.CurrentData = extExecuteData.NewRecord;
                    changeLogContext.PrevData = PrevRecord;
                }
                var changeLog = log.LogChangeSet(changeLogContext);
                if (changeLog != null)
                    changeRecords.Add(changeLog);
            }
            else if (InjectMethodType == SPI.InjectionMethodType.Delete)
            {
                if (!string.IsNullOrEmpty(invokeContext.SourceDataID))
                {
                    changeLogContext.CurrentPK = invokeContext.SourceDataID;
                    changeRecords.Add(log.LogChangeSet(changeLogContext));
                }
                else if (invokeContext.SourceDataIDs != null)
                {
                    foreach (var pk in invokeContext.SourceDataIDs)
                    {
                        changeLogContext.CurrentPK = pk;
                        changeRecords.Add(log.LogChangeSet(changeLogContext));
                    }
                }
                //因为主节点为删除的操作返回的Record是生成的，所以暂存一下引用，方便后续调用
                invokeContext.RecordData(PluginConst.ChangeLog.Code, PluginConst.ChangeLog.DeleteRecord, changeRecords);
            }
            return changeRecords;
        }

        /// <summary>
        /// 获取大数据变更日志
        /// </summary>
        /// <param name="InjectMethodType"></param>
        /// <param name="extExecuteData"></param>
        private List<DataChangeLog> GetBigDataChangeLogs(InjectionMethodType InjectMethodType, DataAccessPluginExtParam extExecuteData)
        {
            var log = ChangeLogFactory.GetInstance(InjectMethodType);
            List<DataChangeLog> dataChangeLogs = new List<DataChangeLog>();
            ChangeLogContext changeLogContext = new ChangeLogContext
            {
                CompositeDataObject = invokeContext.SourceMeta as CompositeDataObject,
                InjectionMethodType = InjectMethodType,
            };
            changeLogContext.InitAuthControl(invokeContext.IsNeedAuth, invokeContext.ColumnPermissionResult);
            if (InjectMethodType == SPI.InjectionMethodType.Create)
            {
                if (extExecuteData?.NewRecord != null)
                {
                    changeLogContext.CurrentData = extExecuteData?.NewRecord;
                }
                var changeLog = log.LogBigDataChangeLog(changeLogContext);
                if (changeLog != null)
                    dataChangeLogs.Add(changeLog);
            }
            else if (InjectMethodType == SPI.InjectionMethodType.Update)
            {
                if (extExecuteData?.NewRecord != null)
                {
                    changeLogContext.CurrentData = extExecuteData?.NewRecord;
                    changeLogContext.PrevData = PrevRecord;
                }
                var changeLog = log.LogBigDataChangeLog(changeLogContext);
                if (changeLog != null)
                    dataChangeLogs.Add(changeLog);
            }
            else if (InjectMethodType == SPI.InjectionMethodType.Delete)
            {
                if (!string.IsNullOrEmpty(invokeContext.SourceDataID))
                {
                    changeLogContext.CurrentPK = invokeContext.SourceDataID;
                    dataChangeLogs.Add(log.LogBigDataChangeLog(changeLogContext));
                }
                else if (invokeContext.SourceDataIDs != null)
                {
                    foreach (var pk in invokeContext.SourceDataIDs)
                    {
                        changeLogContext.CurrentPK = pk;
                        dataChangeLogs.Add(log.LogBigDataChangeLog(changeLogContext));
                    }
                }
            }
            return dataChangeLogs;
        }

        /// <summary>
        /// 异步记录日志
        /// </summary>
        /// <param name="dataChangeLogs"></param>
        private void SendDataChangeLogs(object dataChangeLogs)
        {
            try
            {
                List<DataChangeLog> logs = new List<DataChangeLog>();
                if (dataChangeLogs is List<TRecord> changeRecords)
                {
                    //由于TChangeLog序列化屏蔽掉了Data子节点，转换为大数据日志后记录
                    foreach (var changeRecord in changeRecords)
                    {
                        string recordStr = JsonConvert.SerializeObject(changeRecord, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
                        _localLogService.Write($"CDO变更日志记录调试，record:{changeRecord.TimestampColumnName}\r\n{recordStr}\r\n{TRecord.ToJson(changeRecord)}");

                        var dataChangeLog = changeRecord.GetAllDataChangeLog();
                        if (dataChangeLog != null)
                        {
                            _localLogService.Write($"CDO变更日志记录调试，log:{dataChangeLog.CreateTime}\r\n{JsonConvert.SerializeObject(dataChangeLog)}");
                            logs.Add(dataChangeLog);
                        }
                    }
                }
                else if (dataChangeLogs is List<TChangeLog> changeSets)
                {
                    //由于TChangeLog序列化屏蔽掉了Data子节点，转换为大数据日志后记录
                    foreach (var changeSet in changeSets)
                    {
                        logs.Add(changeSet.ConvertToBigDataChangeLog());
                    }
                }
                else if (dataChangeLogs is List<DataChangeLog> changeLogs)
                {
                    logs = changeLogs.Select(l => l as DataChangeLog).ToList();
                }

                if (logs.Count > 0)
                {
                    _localLogService.WriteChangeLog(logs);
                    //调用新的FasLog服务
                    FasLogService fasLogService = FasLogService.GetInstance();
                    fasLogService.AddLogs(logs);
                    
                }
            }
            catch (Exception e)
            {
                _localLogService.WriteError(e);
            }
        }

        private void SendDataChangeLogsAsync<T>(List<T> dataChangeLogs)
        {
            TaskFactory.StartNew((logs) => SendDataChangeLogs(logs), dataChangeLogs, TaskCreationOptions.None);
        }

        /// <summary>
        /// 回写ChangeSet，供SM使用
        /// </summary>
        /// <param name="changeLogs"></param>
        private void DoSomething(List<TChangeLog> changeLogs)
        {
            //回写ChangeSet
            invokeContext.RecordData(PluginCode, PluginConst.ChangeLog.ChangeSet, changeLogs?.FirstOrDefault());
            //异步推送大数据日志
            SendDataChangeLogsAsync(changeLogs);
        }

        /// <summary>
        /// 回写ChangeSet，供SM使用
        /// </summary>
        /// <param name="changeRecords"></param>
        private void DoSomething(List<TRecord> changeRecords)
        {
            //异步推送大数据日志
            SendDataChangeLogsAsync(changeRecords);
        }
        #endregion

        /// <summary>
        /// 设置变更字段信息，使用TRecord.SetValue，使之体现在TRecord.ChangeSet中
        /// </summary>
        /// <param name="metadata"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        private TRecord SetModifyInfo(CompositeDataObject metadata, TRecord data)
        {
            string timestampColumnName = metadata.RootCompositeNodeObject?.GetTimestampElement()?.DataColumn?.ColumnName;
            if (string.IsNullOrEmpty(timestampColumnName))
            {
                if (data.IsContainProperty(DataModelSystemConst.LastModifyTime))
                    timestampColumnName = DataModelSystemConst.LastModifyTime;
            }
            //如果时间戳字段不存在，跳过
            if (!string.IsNullOrEmpty(timestampColumnName))
            {
                if (string.IsNullOrEmpty(data.TimestampColumnName))
                {
                    data.TimestampColumnName = timestampColumnName;
                }
                //throw new FasException(DataAccessErrorCode.CompositeDataObject.NoTimestampField, $"CDO:{metadata.Code}主节点找不到时间戳字段");
                //在线程上下文中获取时间戳，拿到DAC Service注册的时间戳
                var time = DACContextHelper.GetTimestamp();
                /* 
                 * 设置最后修改时间，最后修改人&姓名通过变更日志本身去判断，因为时间戳字段是在DAC.Update中新注册的时间戳，所以需要特殊处理。
                 * 最后修改人是通过Session中获取的，在Action中获取变更日志时，跟DAC是一个线程，变更内容不会有变化。
                 * 只需要修改最后修改人
                 */
                data.SetValue(timestampColumnName, new Timestamp { Data = time.ToString(Timestamp.DefaultTimeFormat), Value = time });
                if (data.ChangeSet != null)
                    data.ChangeSet.ModifyTime = time;
                ////设置最后修改人
                //if (data.IsContainProperty(DataModelSystemConst.LastModifier))
                //    data.SetValue(DataModelSystemConst.LastModifier, UserUtil.GetCurrentUserID());
                ////设置最后修改人姓名
                //if (data.IsContainProperty(DataModelSystemConst.LastModifierName))
                //    data.SetValue(DataModelSystemConst.LastModifierName, UserUtil.GetCurrentUserName());
            }
            return data;
        }
    }

    public static class ChangeLogExtension
    {
        /// <summary>
        /// 填充变更日志中的审计字段（最后修改人，最后修改时间）
        /// </summary>
        /// <param name="changeLog"></param>
        /// <param name="record">最后修改时间，后端取的TRecord为Timestamp类型，前端传入的为Json对象或字符串，需要转为Timestamp</param>
        public static void FillDefaultField(this DataChangeLog changeLog, TRecord record)
        {

        }

        public static DataChangeLog ConvertToBigDataChangeLog(this TChangeLog changeSet)
        {
            //映射变更
            DataChangeLog dataChangeLog = new DataChangeLog();
            dataChangeLog.BillType = changeSet.CDOCode;
            dataChangeLog.BillName = changeSet.CDOName;
            dataChangeLog.BillID = changeSet.BillID;                   //单据id
            dataChangeLog.Action = changeSet.DataChangeType.ToString();//主单据操作类型，如果只有子节点变更，也应填入Update类型
            dataChangeLog.UserID = changeSet.Modifier;
            dataChangeLog.Ext15 = changeSet.CDOID;                   //CDO.ID
            dataChangeLog.Ext16 = changeSet.TenantID;                //租户id
            dataChangeLog.Ext17 = changeSet.CDOCode;
            dataChangeLog.Ext18 = changeSet.CDOName;
            dataChangeLog.Ext19 = changeSet.ModifierName;            //修改人名称
            dataChangeLog.CreateTime = DateTime.Now;         //删除时间获取当前服务器时间即可
            dataChangeLog.Datas = new List<DataChangeItem>();

            if (changeSet.ChangeDatas != null && changeSet.ChangeDatas.Count > 0)
            {
                foreach (TChangeData data in changeSet.ChangeDatas)
                {
                    DataChangeItem item = new DataChangeItem();
                    item.ID = data.ColumnCode;
                    item.SID = data.ColumnCode;              //节点Alias
                    item.DataColumn = data.ColumnCode;
                    //表示当前变更节点不为List或Record
                    if (data.ChangeDataType == ChangeDataType.Normal)
                    {
                        item.NewData = data.NewValue?.ToString();
                        item.OldData = data.OldValue?.ToString();
                    }
                    else    //除了Normal类型外，序列化后放到NewValue中
                    {
                        if (data.ChangeDataType == ChangeDataType.List && data.List != null)
                        {
                            List<DataChangeLog> childLogs = new List<DataChangeLog>();
                            foreach (var childSet in data.List)
                            {
                                var log = childSet.ConvertToBigDataChangeLog();
                                if (log != null)
                                    childLogs.Add(log);
                            }
                            item.NewData = JsonConvert.SerializeObject(childLogs);     //子级及更下级的节点记录序列化json
                        }
                        if (data.ChangeDataType == ChangeDataType.Record && data.Record != null)
                        {
                            var log = data.Record.ConvertToBigDataChangeLog();
                            if (log != null)
                            {
                                item.NewData = JsonConvert.SerializeObject(log);     //子级及更下级的节点记录序列化json
                            }
                        }
                    }
                    dataChangeLog.Datas.Add(item);
                }
            }

            return dataChangeLog;
        }
    }
}
